package ooo.reindeer.reflect;

import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;

public class InstanceProxy {
    public static <T> T instance(final T entity){
        ProxyFactory proxyFactory = new ProxyFactory();

        // 设置被代理类的类型
        proxyFactory.setSuperclass(entity.getClass());

        // 创建代理类的class
        Class proxyClass = proxyFactory.createClass();

        // 创建对象
        ProxyObject proxy = null;
        try {
            proxy = (ProxyObject) proxyClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw  new RuntimeException(e);
        }

        ProxyHandler<T> proxyHandler = new ProxyHandler<T>(proxy, entity);
        //注册代理
        proxy.setHandler(proxyHandler);

        return (T) proxy;
    }

    public static <T> T unProxy(T entity) {
        if (entity instanceof ProxyObject) {
            return (T) ((ProxyHandler) ((ProxyObject) entity).getHandler()).getSrcEntity();
        } else {
            return entity;
        }

    }

    public static <T> void registeProxyMethodHandler(T entity, String methodName, ProxyMethodHandler<T> proxyMethodHandler) {
        registeProxyMethodHandler(entity, methodName, new Class[0], proxyMethodHandler);
    }

    public static <T> void registeProxyMethodHandler(T entity, String methodName, Class[] paramTypes, ProxyMethodHandler<T> proxyMethodHandler) {
        if (entity instanceof ProxyObject) {
            if (paramTypes == null) {
                paramTypes = new Class[0];
            }
            ((ProxyHandler) ((ProxyObject) entity).getHandler()).registeProxyMethodHandler(methodName, paramTypes, proxyMethodHandler);
        } else {

        }
    }

    public static class ProxyHandler<T> implements MethodHandler {

        ConcurrentHashMap<String, ProxyMethodHandler> proxyHandlerConcurrentHashMap = new ConcurrentHashMap<String, ProxyMethodHandler>();

        T srcEntity;

        ProxyObject proxyObject;

        public void registeProxyMethodHandler(String methodName, Class[] paramTypes, ProxyMethodHandler<T> proxyMethodHandler) {
            String key = methodName + ":" + Arrays.toString(paramTypes);
            proxyHandlerConcurrentHashMap.put(key, proxyMethodHandler);
        }

        T getSrcEntity() {
            return srcEntity;
        }

        ProxyHandler(ProxyObject proxyObject, T srcEntity) {
            this.srcEntity = srcEntity;
            this.proxyObject = proxyObject;
        }

        @Override
        public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
            String key = thisMethod.getName() + ":" + Arrays.toString(thisMethod.getParameterTypes());
            ProxyMethodHandler proxyHandler = proxyHandlerConcurrentHashMap.get(key);
            if (proxyHandler == null) {
                return thisMethod.invoke(srcEntity, args);
            } else {
                try {
                    proxyHandler.before(self, thisMethod, args);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                Object ret = thisMethod.invoke(srcEntity, args);

                try {
                    ret = proxyHandler.after(self, thisMethod, ret, args);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return ret;
            }

        }


    }
}
